/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package hu.kovacsp.scala.discovery.examples.di

/**
 * http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di.html
 *
 */
object CakePattern extends ComponentRegistry {
  def main(args: Array[String]) {
    println("Cake Pattern, User example:")
    val user = userService.create("create", "password")
    val authenticatedUser = userService.authenticate("authenticate", "password")
    println("done")

    // =======================
    println("Cake Pattern, Heater example:")
    val warmer = ComponentRegistry.warmer
    warmer.trigger
    println("done")
  }
}

case class User(username: String, password: String) {}

// a dummy service that is not persisting anything
// solely prints out info
class UserRepository {
  def authenticate(user: User): User = {
    println("authenticating user: " + user)
    user
  }
  def create(user: User) = println("creating user: " + user)
  def delete(user: User) = println("deleting user: " + user)
}

trait UserRepositoryComponent {
  val userRepository : UserRepository
  class UserRepository {
    def authenticate(user: User): User = {
      println("authenticating user: " + user)
      user
    }
    def create(user: User) = println("creating user: " + user)
    def delete(user: User) = println("deleting user: " + user)
  }
}

// using self-type annotation declaring the dependencies this
// component requires, in our case the UserRepositoryComponent
trait UserServiceComponent { this: UserRepositoryComponent =>
  val userService: UserService
  class UserService {
    def authenticate(username: String, password: String): User =
      userRepository.authenticate(new User(username, password))
    def create(username: String, password: String) =
      userRepository.create(new User(username, password))
    def delete(user: User) = userRepository.delete(user)
  }
}

trait ComponentRegistry extends
UserServiceComponent with
UserRepositoryComponent
{
  val userRepository = new UserRepository
  val userService = new UserService
}

/**
 * ============================ CAKE PATTERN ===================================
 */

// =======================
// service interfaces
trait OnOffDeviceComponent {
  val onOff: OnOffDevice
  trait OnOffDevice {
    def on: Unit
    def off: Unit
  }
}
trait SensorDeviceComponent {
  val sensor: SensorDevice
  trait SensorDevice {
    def isCoffeePresent: Boolean
  }
}

// =======================
// service implementations
trait OnOffDeviceComponentImpl extends OnOffDeviceComponent {
  class Heater extends OnOffDevice {
    def on = println("heater.on")
    def off = println("heater.off")
  }
}
trait SensorDeviceComponentImpl extends SensorDeviceComponent {
  class PotSensor extends SensorDevice {
    def isCoffeePresent = true
  }
}
// =======================
// service declaring two dependencies that it wants injected
trait WarmerComponentImpl {
  this: SensorDeviceComponent with OnOffDeviceComponent =>
  class Warmer {
    def trigger = {
      if (sensor.isCoffeePresent) onOff.on
      else onOff.off
    }
  }
}

// =======================
// instantiate the services in a module
object ComponentRegistry extends
  OnOffDeviceComponentImpl with
  SensorDeviceComponentImpl with
  WarmerComponentImpl {

  val onOff = new Heater
  val sensor = new PotSensor
  val warmer = new Warmer
}



